/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file player.cpp
 * @author TooOld2Rock'nRoll
 * @see player.hpp
 */
/*---- Includes ----*/
#include "player.hpp"

#include "toolbox/debug.h"


/*---- Global Variables ----*/


/*---- Methods Implementation ----*/
/**
 * @brief Player Base Constructor.
 */
Player::Player ()
{
    int i = 0;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    //Setup event listener
    this->evMngInstance = &EventManager::instance ();
    this->lsID = this->evMngInstance->addListener ();

    for (i = this->u_action_array_size; i--; )
        this->ap_action_input[i] = nullptr;
}//End Constructor


/**
 * @brief Destructor.
 */
Player::~Player ()
{
    this->evMngInstance->removeListener (this->lsID);
}//end Destructor


/**
 * @brief Maps a know key to a input event.
 *
 * @param in - the "controller" action.
 * @param key - the scancode of the key to map.
 * @param mod - any keyboard modifiers pertinent to this input action.
 *
 * @return 0 if succeeded, error otherwise.
 * @retval 0 - Key map valid and saved.
 * @retval 1 - input key out of range.
 * @retval 2 - key already mapped.
 */
int Player::mapInput (controller_button_map_t in, SDL_Scancode key, SDL_Keymod mod)
{
    //our array is 3 times bigger!
    unsigned pos = in * key_state::_key_state_max;
    int error = 0,
        i = 0;

    _debug (TRACE, "::%s (input=%u, origin='keyboard')", __FUNCTION__, in);

    if (in >= controller_button_map_t::_button_max)
    {
        _debug (ERROR, "input value out of range!");
        return 1;
    }//end if (in >= Player::_input_max)
    else if (this->ap_action_input[pos])
    {
        _debug (WARNING, "Event already set, can't override!");
        return 2;
    }//end else if (this->_action_input[pos].type != EventManager::event_type::_event_type_max)

    //each key has 3 valid states
    //and we save the position the event is saved to retrieve the index when receiving it.
    for (i = key_state::_key_state_max; i--; )
        this->ap_action_input[pos + i] = this->evMngInstance->listenForEvent (this->lsID, (key_state)(i), key, mod, (void *)&this->ap_action_input[pos + i]);

    return error;
}//End mapInput ()


/**
 * @brief Handles player input.
 * @see controller_button_map_t
 * @remark You may want to extend this method to handle START and SELECT buttons, since they can have different
 *            interpretations depending on context and we can't offer a generic solution to it.
 *
 * Translates whatever is mapped as input to a more standard format.<br>
 * Buttons START and SELECT (and combinations containing suck keys) are ignored, all the other action buttons are
 *    forwarded to the registered Character the player is controlling (if any).
 *
 * @param delta_t - time elapsed since last game loop iteration.
 */
void Player::processEvents (double delta_t)
{
    unsigned i = 0;
    static const EventManager::event_s *ev = nullptr;

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    if (this->p_puppet)
    {
        while ((ev = this->evMngInstance->pop (this->lsID)))
        {
            if (!ev->p_user_package)
            {
                _debug (ERROR, "The event received by this player is not valid!");
                continue;
            }//end if (!ev->p_user_package)

            //we don't care for the actual event, we can reconstruct the original map from its data.
            //recalculate the event index based on the array address!
            i = ((unsigned long)ev->p_user_package - (unsigned long)&this->ap_action_input[0]) / sizeof(EventManager::event_s *);

            ///@bug Must check index range first!
            if (!this->ap_action_input[i])
                continue;
            else if (i >= this->u_action_array_size)
            {
                _debug (ERROR, "The index of the event received is outside the expected value!");
                continue;
            }//end else if (i >= this->u_action_array_size)

            this->p_puppet->handleInput ((controller_button_map_t)(i / key_state::_key_state_max),
                                         (key_state)(i % key_state::_key_state_max),
                                         delta_t);
        }//end while (this->evMngInstance.fetchEvent (this->lsID))
    }//end if (this->p_puppet)
    else
        while (this->evMngInstance->pop (this->lsID))
        { }//player is not controlling any character, just empty the queue....
}//End processEvents ()

/**
 * @brief Update the player state.
 *
 * @param delta_t - time elapsed since last game loop iteration.
 */
void Player::update (double delta_t)
{
}//End update

/**
 * @brief Draw player objects over all other game objects in the screen.
 *
 * @param delta_t - time elapsed since last game loop iteration.
 */
void Player::draw (double delta_t)
{
}//End draw ()
